#ifndef _DAG_THREADING_H_
#define _DAG_THREADING_H_

#include <stdio.h>
#include <map>
#include <set>
#include "dag.h"

#ifdef WIN32
#	include <windows.h>
#	define DAG_WIN32_THREADS
#else
#	define DAG_POSIX_THREADS
#	include <pthread.h>
#endif

namespace dag
{	
	/* define sleep call */
#ifdef DAG_WIN32_THREADS
	inline void dag_msleep(unsigned ms) {
		Sleep(ms);
	}
#else
	inline void dag_msleep(unsigned ms) { 
		usleep(ms * 1000);
	}
#endif
	
#ifdef DAG_WIN32_THREADS
	/* struct for cond. variable impl. on win32 */
	struct DAG_API pthread_cond_t {
		/* Number of waiting threads. */
		int m_WaiterCount;
		
		/* Serialize access to <m_WaiterCount>. */
		CRITICAL_SECTION m_WaiterCountLock;

		/* Semaphore used to queue up threads waiting for the condition to
		 * become signaled. */
		HANDLE m_Semaphore;

		/* An auto-reset event used by the broadcast/signal thread to wait
		 * for all the waiting thread(s) to wake up and be released from the
		 * semaphore. */
		HANDLE m_WaitersDone;

		/* Keeps track of whether we were broadcasting or signaling.  This
		 * allows us to optimize the code if we're just signaling. */
		size_t m_WasBroadcast;
	};
	
	typedef CRITICAL_SECTION pthread_mutex_t;
	typedef HANDLE ThreadID;
#else
	typedef pthread_t ThreadID;
#endif
	
	
	class DAG_API Mutex {
	public:
		
		friend class Condition;
		Mutex(const char* label = 0);//label should be descriptive to aid debugging
		~Mutex();
		
		void lock(void);
		bool tryLock();
		void unlock(void);
		
	protected:
		
#ifndef DAG_WIN32_THREADS
		pthread_mutex_t m_mutexVar;  /* mutex variable */
#else
		CRITICAL_SECTION m_mutexVar;
#endif
	};
	
	class DAG_API Condition {
	public:
		Condition();
		~Condition();
		
		void signal();
		void broadcast();
		
		void wait(Mutex& mutexVar);
		void wait();
		void wait(struct timespec* expiration);
		
	protected:
		pthread_cond_t m_condVar;
	};
	
	class DAG_API ScopedLock {
		ScopedLock();
		ScopedLock(const ScopedLock& copy);
		ScopedLock& operator=(const ScopedLock& rhs);
	public:
		
		ScopedLock(Mutex& mutex);
		virtual ~ScopedLock();
		
	private:
		Mutex& m_Mutex;
	};
	
	class DAG_API ScopedTryLock {
		ScopedTryLock();
		ScopedTryLock(const ScopedTryLock& copy);
		ScopedTryLock& operator=(const ScopedTryLock& rhs);
		
	public:
		ScopedTryLock(Mutex& mutex);
		virtual ~ScopedTryLock();
		
		bool success() const throw();
		
	private:
		Mutex& m_Mutex;
		bool m_Success;
	};
	
	
	class DAG_API Thread {
		
	public:
		
		Thread();
		~Thread();
		
		int create(const char* label,
				   void* (*func)(void*),
				   void* param,
				   bool detach = false,
				   bool asynchron = false);
		void cancel();
		void join(void** = 0);
		int detach();
		const ThreadID& id() const;
		
	protected:
		ThreadID m_threadId;
		
#ifdef DAG_WIN32_THREADS
		DWORD  m_thread1;
#endif
	};
	
	
	class DAG_API Barrier {
	public:
		Barrier(int _count);
		~Barrier();
		
		void setCount(int count);
		void lock();
		void unlock();
		void wait();
		void reached();
		
	private:
		
		Condition m_cond;
		int m_count;
		Mutex m_mutex;
	};
	
	class DAG_API Runnable {
		Runnable(const Runnable& copy);
		Runnable& operator=(const Runnable& rhs);
	public:

		Runnable();
		virtual ~Runnable();
		
		void startThread();
		virtual void runThread() = 0;
		void cancelThread();
		void joinThread();
	private:
		Thread* m_Thread;
	};
	void usleep(unsigned us);
}
#endif